---
title: Meshlines
---

This example was inspired by [OGL's Polyline example](https://oframe.github.io/ogl/examples/?src=polylines.html). It uses [`<MeshLineMaterial>`](/docs/reference/extras/meshline-material) and [`<MeshLineGeometry>`](/docs/reference/extras/meshline-geometry) to create a similar effect.

<Tip type="tip">
  This effect is probably better implemented as a fragment shader but the example highlights some
  interesting and effective techniques for various threlte components and functions.
</Tip>

<Example path="meshline/cursor-lines" />

## How Does it Work?

First we create a scene with an orthographic camera and a mesh. The mesh's only purpose is to capture pointer move events so it needs to be big enough to fill the entire screen. The mesh is placed in front of the camera. It doesn't really matter the exact position of the mesh because an orthographic camera has no perspective. All that really matters it is in fron of the camera and there is space inbetween the camera and the mesh - this is where the mesh lines will be drawn.

```svelte
<T.OrthographicCamera
  zoom={50}
  makeDefault
/>

<T.Mesh
  scale={100}
  visible={false}
  position.z={-10}
>
  <T.PlaneGeometry />
</T.Mesh>
```

The camera is positioned at the origin and the mesh is positioned down the z-axis.

### Getting the Cursor Position

To get the cursor position we use Threlte's [interactivity](/docs/reference/extras/interactivity) plugin. The event object that is passed to the `onpointermove` callback has a point property which tells you where the cursor position was when the event was triggered. The cursor position is updated and sent into the `<CursorLine>` component as a prop.

```svelte
<script lang="ts">
  const cursorPosition = new Spring<Vector3Tuple>([0, 0, 0])
  interactivity()
</script>

<T.Mesh
  onpointermove={(event) => {
    cursorPosition = event.point.toArray()
  }}
>
  <!-- ...  -->
</T.Mesh>
```

### Inside the CursorLine Component

The `<CursorLine>` component receives the current pointer position. To create the "trailing" effect two lists of `Vector3` points are created - a back and a front. Each frame, the back set of points is updated then swapped with the front set of points. This swap causes anything that reads from the front set of points to get updated.

#### Making Use of $state.raw

The reason that two sets of points are used is so that [`$state.raw`](https://svelte.dev/docs/svelte/$state#$state.raw) can be used. `$state.raw` is great when you have a large object such as an array and you don't want to make the object deeply reactive. In our case we know we're only ever going to be updating everything in the array all at once and nothing depends on updates to the individual objects in the array. In other words, we only care about when the entire array updates, specific when `front` updates. The back set of points doesn't need to be made reactive at all because nothing is listening to it.

#### Updating Points

Before the back and front points are swapped, the points are updated. The first point in the array is set to the current value of the spring.

```typescript
back[0].fromArray(spring.current)
```

Then each consecutive pair of points, `[first, secend]` is taken and the second point is interpolated to the first by a small amount. This update happens every frame and eventually all points are interpolated to the cursor position.

```typescript
useTask((delta) => {
  const alpha = 1e-6 ** delta
  for (let i = 1; i < count; i += 1) {
    const first = back[i - 1]
    const second = back[i]
    second?.lerp(first, alpha)
  }
  // ...
})
```

<Tip type="info">
  The value for `alpha` is a little arbitrary but certain values may look may appealing than others.
  For example, if `alpha` is > 1, the lerp will overshoot and you'll get very "jumpy" interpolations
  that won't ever settle. You also don't want the value to be too small because then it won't
  interpolate quickly enough to look "smooth". An value somewhere around `.8` gives a good look and
  feel.
</Tip>

Lastly the two point-lists are swapped which triggers anything reading `front` to update.

```typescript
useTask((delta) => {
  //...
  const temp = front
  front = back
  back = temp
})
```

In summary, we update the "back" set of points and swap it with the "front" once all points have been updated. This is very similar to a the double-buffering strategy used by many graphics software.

A "getter" for the points is available in the `children` snippet so that its current value can be used.

```svelte
<CursorLine>
  {#snippet children({ getPoints })}
    <MeshLineGeometry
      points={getPoints()}
      shape="taper"
    />
    <MeshLineMaterial attenuate={false} />
  {/snippet}
</CursorLine>
```
